import java.awt.Color;
import java.awt.GridLayout;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;

import javax.swing.*;

public class Othello extends JFrame{

	private static final long serialVersionUID = 1L;
	private JButton[][] buttons = new JButton[8][8];
	private ClickListener listen = new ClickListener(this);
	protected boolean black = true;
	private int bScore = 2;
	private int wScore = 2;
	private int turn = 0;
	private JPanel board = new JPanel();
	private JLabel title = new JLabel("Scoreboard       ");
	private JLabel bScoreOut = new JLabel("Black: " + bScore + "        ");
	private JLabel wScoreOut = new JLabel("White: " + wScore + "        ");
	private JLabel turnOut = new JLabel("Black's turn");
	private JMenuBar bar = new JMenuBar();
	private JMenu menu = new JMenu("Menu        ");
	private JRadioButtonMenuItem button = new JRadioButtonMenuItem("AI");
	private JFileChooser fp = new JFileChooser();
	protected JMenuItem load = new JMenuItem("Load Board");
	protected JMenuItem save = new JMenuItem("Save Board");
	protected JMenuItem reset = new JMenuItem("Reset Game");
	protected JMenuItem exit = new JMenuItem("Exit");

	public Othello(){
		setLocation(50, 50);
		setup();
	}
	
	public Othello(int x, int y){
		setLocation(x,y);
		setup();
	}
	
	private void setup(){
		board.setLayout(new GridLayout(8,8));
		load.addActionListener(listen);
		save.addActionListener(listen);
		reset.addActionListener(listen);
		exit.addActionListener(listen);
		menu.add(button);
		menu.add(load);
		menu.add(save);
		menu.add(reset);
		menu.add(exit);
		bar.add(menu);
		bar.add(title);
		bar.add(bScoreOut);
		bar.add(wScoreOut);
		bar.add(turnOut);
		setJMenuBar(bar);
		setSize(500,500);
		setResizable(false);
		setTitle("Othello - By Parker Ross");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		initButtons();
		add(board);
		setVisible(true);
	}

	private void initButtons(){
		for(int i = 0; i < buttons.length; i++){
			for(int j = 0; j < buttons[i].length; j++){
				JButton b = new JButton();
				b.setBackground(Color.GREEN);
				b.setName(i + "," + j);
				b.addActionListener(listen);
				buttons[i][j] = b;
				board.add(b);
			}
		}
		buttons[3][4].setBackground(Color.BLACK);
		buttons[4][3].setBackground(Color.BLACK);
		buttons[3][3].setBackground(Color.WHITE);
		buttons[4][4].setBackground(Color.WHITE);
	}

	protected void buttonClick(JButton b){
		if(b.getBackground().equals(Color.BLACK)||b.getBackground().equals(Color.WHITE))
			return;
		String[] x = b.getName().split(",");
		if(checkLegal(Integer.parseInt(x[0]), Integer.parseInt(x[1])))
			takeTurn(b);
		else
			JOptionPane.showMessageDialog(this, "That is not a valid move");
	}

	private boolean checkLegal(int r, int c){
		return count(buttons[r][c]) > 0;
	}

	private void checkValid(){
		boolean prev = black;
		boolean bVal;
		boolean wVal;
		int moves = 0;
		black = false;
		for(int i = 0; i < buttons.length; i++){
			for(int j = 0; j < buttons[i].length; j++){
				if(checkLegal(i,j))
					moves++;
			}
		}
		if(moves==0)
			wVal = false;
		else
			wVal = true;
		black =  true;
		moves = 0;
		for(int i = 0; i < buttons.length; i++){
			for(int j = 0; j < buttons[i].length; j++){
				if(checkLegal(i,j))
					moves++;
			}
		}
		black = prev;
		if(moves==0)
			bVal = false;
		else
			bVal = true;

		if(!bVal && !wVal)
			endGame();
		else if(!bVal && wVal)
			AITurn();
		return;
	}

	private void takeTurn(JButton b){
		if(black){
			b.setBackground(Color.BLACK);
			bScore++;
			flip(b);
			black = false;
		}
		else{
			b.setBackground(Color.WHITE);
			wScore++;
			flip(b);
			black = true;
		}
		updateScores();
		checkValid();
		turn ++;
		if(turn == 60)
			endGame();
		if(!black && button.isSelected())
			AITurn();
	}

	private void flip(JButton b){
		int flipTotal = 0;
		String[] x = b.getName().split(",");
		int r = Integer.parseInt(x[0]);
		int c = Integer.parseInt(x[1]);
		flipTotal += flipL(b,r,c);
		flipTotal += flipR(b,r,c);
		flipTotal += flipU(b,r,c);
		flipTotal += flipD(b,r,c);
		flipTotal += flipUR(b,r,c);
		flipTotal += flipDR(b,r,c);
		flipTotal += flipDL(b,r,c);
		flipTotal += flipUL(b,r,c);

		if(black){
			bScore += flipTotal;
			wScore -= flipTotal;
		}
		else{
			wScore += flipTotal;
			bScore -= flipTotal;
		}
	}

	private int flipL(JButton b, int r, int c){
		ArrayList<JButton> toFlip = new ArrayList<JButton>();
		Color color = b.getBackground();
		int count = 0;
		c--;
		for(;c>=0;c--){
			if(buttons[r][c].getBackground().equals(Color.GREEN))
				return 0;
			else if(buttons[r][c].getBackground().equals(color)){
				changeColor(toFlip);
				return count;
			}
			else{
				count++;
				toFlip.add(buttons[r][c]);
			}
		}
		return 0;
	}

	private int flipR(JButton b, int r, int c){
		ArrayList<JButton> toFlip = new ArrayList<JButton>();
		Color color = b.getBackground();
		int count = 0;
		c++;
		for(;c<=7;c++){
			if(buttons[r][c].getBackground().equals(Color.GREEN))
				return 0;
			else if(buttons[r][c].getBackground().equals(color)){
				changeColor(toFlip);
				return count;
			}
			else{
				count++;
				toFlip.add(buttons[r][c]);
			}
		}
		return 0;
	}

	private int flipU(JButton b, int r, int c){
		ArrayList<JButton> toFlip = new ArrayList<JButton>();
		Color color = b.getBackground();
		int count = 0;
		r--;
		for(;r>=0;r--){
			if(buttons[r][c].getBackground().equals(Color.GREEN))
				return 0;
			else if(buttons[r][c].getBackground().equals(color)){
				changeColor(toFlip);
				return count;
			}
			else{
				count++;
				toFlip.add(buttons[r][c]);
			}
		}
		return 0;
	}

	private int flipD(JButton b, int r, int c){
		ArrayList<JButton> toFlip = new ArrayList<JButton>();
		Color color = b.getBackground();
		int count = 0;
		r++;
		for(;r<=7;r++){
			if(buttons[r][c].getBackground().equals(Color.GREEN))
				return 0;
			else if(buttons[r][c].getBackground().equals(color)){
				changeColor(toFlip);
				return count;
			}
			else{
				count++;
				toFlip.add(buttons[r][c]);
			}
		}
		return 0;
	}

	private int flipUR(JButton b, int r, int c){
		ArrayList<JButton> toFlip = new ArrayList<JButton>();
		Color color = b.getBackground();
		int count = 0;
		r--;
		c++;
		for(;r>=0 && c<=7;r--,c++){
			if(buttons[r][c].getBackground().equals(Color.GREEN))
				return 0;
			else if(buttons[r][c].getBackground().equals(color)){
				changeColor(toFlip);
				return count;
			}
			else{
				count++;
				toFlip.add(buttons[r][c]);
			}
		}
		return 0;
	}

	private int flipDR(JButton b, int r, int c){
		ArrayList<JButton> toFlip = new ArrayList<JButton>();
		Color color = b.getBackground();
		int count = 0;
		r++;
		c++;
		for(;r<=7 && c<=7;r++,c++){
			if(buttons[r][c].getBackground().equals(Color.GREEN))
				return 0;
			else if(buttons[r][c].getBackground().equals(color)){
				changeColor(toFlip);
				return count;
			}
			else{
				count++;
				toFlip.add(buttons[r][c]);
			}
		}
		return 0;
	}

	private int flipDL(JButton b, int r, int c){
		ArrayList<JButton> toFlip = new ArrayList<JButton>();
		Color color = b.getBackground();
		int count = 0;
		r++;
		c--;
		for(;r<=7 && c>=0;r++,c--){
			if(buttons[r][c].getBackground().equals(Color.GREEN))
				return 0;
			else if(buttons[r][c].getBackground().equals(color)){
				changeColor(toFlip);
				return count;
			}
			else{
				count++;
				toFlip.add(buttons[r][c]);
			}
		}
		return 0;
	}

	private int flipUL(JButton b, int r, int c){
		ArrayList<JButton> toFlip = new ArrayList<JButton>();
		Color color = b.getBackground();
		int count = 0;
		r--;
		c--;
		for(;r>=0 && c>=0;r--,c--){
			if(buttons[r][c].getBackground().equals(Color.GREEN))
				return 0;
			else if(buttons[r][c].getBackground().equals(color)){
				changeColor(toFlip);
				return count;
			}
			else{
				count++;
				toFlip.add(buttons[r][c]);
			}
		}
		return 0;
	}

	private void changeColor(ArrayList<JButton> flip){
		for(JButton b : flip){
			if(b.getBackground().equals(Color.WHITE))
				b.setBackground(Color.BLACK);
			else
				b.setBackground(Color.WHITE);
		}
	}

	private void endGame(){
		JOptionPane.showMessageDialog(this, "Game Over!\nBlack: " + bScore + "\nWhite: " + wScore);
		reset();
	}

	protected void reset(){
		new Othello(getLocation().x,getLocation().y);
		this.dispose();
	}

	private void AITurn(){
		ArrayList<JButton> possible = new ArrayList<JButton>();
		for(int i = 0; i < buttons.length; i++){
			for (int j = 0; j < buttons[i].length; j++){
				if(buttons[i][j].getBackground().equals(Color.GREEN) && checkLegal(i,j))
					possible.add(buttons[i][j]);
			}
		}
		if(possible.isEmpty()){
			black = true;
			return;
		}		
		Random r = new Random();
		takeTurn(possible.get(r.nextInt(possible.size())));
	}

	private int count(JButton b){
		int countTotal = 0;
		String[] x = b.getName().split(",");
		int r = Integer.parseInt(x[0]);
		int c = Integer.parseInt(x[1]);
		countTotal += countL(b,r,c);
		countTotal += countR(b,r,c);
		countTotal += countU(b,r,c);
		countTotal += countD(b,r,c);
		countTotal += countUR(b,r,c);
		countTotal += countDR(b,r,c);
		countTotal += countDL(b,r,c);
		countTotal += countUL(b,r,c);

		return countTotal;
	}

	private int countL(JButton b, int r, int c){
		Color color = null;
		if(black)
			color = Color.BLACK;
		else
			color = Color.WHITE;
		int count = 0;
		c--;
		for(;c>=0;c--){
			if(buttons[r][c].getBackground().equals(Color.GREEN)){
				return 0;
			}
			else if(buttons[r][c].getBackground().equals(color)){
				return count;
			}
			else{
				count++;
			}
		}
		return 0;
	}

	private int countR(JButton b, int r, int c){
		Color color = null;
		if(black)
			color = Color.BLACK;
		else
			color = Color.WHITE;
		int count = 0;
		c++;
		for(;c<=7;c++){
			if(buttons[r][c].getBackground().equals(Color.GREEN)){
				return 0;
			}
			else if(buttons[r][c].getBackground().equals(color)){
				return count;
			}
			else{
				count++;
			}
		}
		return 0;
	}

	private int countU(JButton b, int r, int c){
		Color color = null;
		if(black)
			color = Color.BLACK;
		else
			color = Color.WHITE;
		int count = 0;
		r--;
		for(;r>=0;r--){
			if(buttons[r][c].getBackground().equals(Color.GREEN)){
				return 0;
			}
			else if(buttons[r][c].getBackground().equals(color)){
				return count;
			}
			else{
				count++;
			}
		}
		return 0;
	}

	private int countD(JButton b, int r, int c){
		Color color = null;
		if(black)
			color = Color.BLACK;
		else
			color = Color.WHITE;
		int count = 0;
		r++;
		for(;r<=7;r++){
			if(buttons[r][c].getBackground().equals(Color.GREEN)){
				return 0;
			}
			else if(buttons[r][c].getBackground().equals(color)){
				return count;
			}
			else{
				count++;
			}
		}
		return 0;
	}

	private int countUR(JButton b, int r, int c){
		Color color = null;
		if(black)
			color = Color.BLACK;
		else
			color = Color.WHITE;
		int count = 0;
		r--;
		c++;
		for(;r>=0 && c<=7;r--,c++){
			if(buttons[r][c].getBackground().equals(Color.GREEN)){
				return 0;
			}
			else if(buttons[r][c].getBackground().equals(color)){
				return count;
			}
			else{
				count++;
			}
		}
		return 0;
	}

	private int countDR(JButton b, int r, int c){
		Color color = null;
		if(black)
			color = Color.BLACK;
		else
			color = Color.WHITE;
		int count = 0;
		r++;
		c++;
		for(;r<=7 && c<=7;r++,c++){
			if(buttons[r][c].getBackground().equals(Color.GREEN)){
				return 0;
			}
			else if(buttons[r][c].getBackground().equals(color)){
				return count;
			}
			else{
				count++;
			}
		}
		return 0;
	}

	private int countDL(JButton b, int r, int c){
		Color color = null;
		if(black)
			color = Color.BLACK;
		else
			color = Color.WHITE;
		int count = 0;
		r++;
		c--;
		for(;r<=7 && c>=0;r++,c--){
			if(buttons[r][c].getBackground().equals(Color.GREEN)){
				return 0;
			}
			else if(buttons[r][c].getBackground().equals(color)){
				return count;
			}
			else{
				count++;
			}
		}
		return 0;
	}

	private int countUL(JButton b, int r, int c){
		Color color = null;
		if(black)
			color = Color.BLACK;
		else
			color = Color.WHITE;
		int count = 0;
		r--;
		c--;
		for(;r>=0 && c>=0;r--,c--){
			if(buttons[r][c].getBackground().equals(Color.GREEN)){
				return 0;
			}
			else if(buttons[r][c].getBackground().equals(color)){
				return count;
			}
			else{
				count++;
			}
		}
		return 0;
	}
	
	private void updateScores(){
		bScoreOut.setText("Black: " + bScore + "        ");
		wScoreOut.setText("White: " + wScore + "        ");
		if(black)
			turnOut.setText("Black's turn");
		else
			turnOut.setText("White's turn");
	}
	
	protected void save(){
		fp.showSaveDialog(this);
		if(fp.getSelectedFile() == null)
			return;
		File file = fp.getSelectedFile();
		if(!file.getPath().endsWith(".txt"))
			file = new File(file.getPath() + ".txt");
		System.out.println(file.getPath());
		try {
			file.createNewFile();
			PrintWriter out = new PrintWriter(file);
			for(int i = 0; i<8;i++){
				for(int j = 0; j<8;j++){
					if(buttons[i][j].getBackground().equals(Color.GREEN))
						out.print(0);
					else if(buttons[i][j].getBackground().equals(Color.WHITE))
						out.print(1);
					else
						out.print(2);
				}
				out.println();
			}
			out.close();
			System.out.println("File created");
		} catch (IOException e) {
			JOptionPane.showMessageDialog(this, "Cannot Access File Location");
		}
		fp.setSelectedFile(null);
	}
	
	protected void loadButtons(){
		fp.showOpenDialog(this);
		if(fp.getSelectedFile() != null){
			Scanner scan = null;
			try {
				scan = new Scanner(fp.getSelectedFile());
				if(!fp.getSelectedFile().toString().endsWith(".txt")){
					JOptionPane.showMessageDialog(this,  "Incorrect Filetype");
					reset();
				}
				int i = 0;
				int lnCount = 0;
				turn = -4;
				bScore = 0;
				wScore = 0;
				for(int x = 0;x<8;x++){
					for(int y = 0;y<8;y++){
						board.remove(buttons[x][y]);
					}
				}
				while(scan.hasNextLine()){
					String line = scan.nextLine();
					lnCount++;
					if(line.length() != 8 || lnCount > 8){
						JOptionPane.showMessageDialog(this, "Error: File is not formatted properly");
						reset();
						scan.close();
						return;
					}
					for(int j = 0; j < line.length();j++){
						JButton button = new JButton();
						button.setName(i + "," + j);
						if(line.charAt(j) == '0'){
							button.setBackground(Color.GREEN);
						}
						else if(line.charAt(j) == '2'){
							button.setBackground(Color.BLACK);
							bScore++;
							turn++;
						}
						else{
							button.setBackground(Color.WHITE);
							wScore++;
							turn++;
						}
						buttons[i][j]=button;
						button.addActionListener(listen);
						board.add(button);
					}
					i++;
				}
			} catch (FileNotFoundException e) {
				JOptionPane.showMessageDialog(this, "Error: File can not be accessed");
			}
			if(turn%2==0)
				black = true;
			updateScores();
			scan.close();
			fp.setSelectedFile(null);
		}
		return;
	}
}
